<?php

namespace app\models\tableModel;

use app\entity\PageListEntity;
use app\models\tableModel\base\Block;
use app\service\CacheService;
use app\service\tool\ConvertService;
use app\service\tool\FileService;
use app\service\tool\GenerateService;
use app\service\tool\UtilsService;
use yii\db\Exception;
use yii\db\Expression;
use yii\db\ExpressionInterface;
use yii\helpers\ArrayHelper;

/**
 * [碎片]数据库操作模型
 * 作者: Editor Name
 * 日期: 2022/01/25
 * 时间: 20:49:10
 */
class BlockModel extends Block
{

    /**
     * 类型 列表
     * @var array
     */
    private static $typeList = [
        [
            'key'   => 'html',
            'value' => 1,
            'text'  => '静态文本'
        ],
        [
            'key'   => 'listHtml',
            'value' => 2,
            'text'  => '模板渲染'
        ]
    ];

    /**
     * 状态 列表
     * @var array
     */
    private static $statusList = [
        [
            'key'   => 'disabled',
            'value' => -1,
            'text'  => '禁用'
        ],
        [
            'key'   => 'open',
            'value' => 1,
            'text'  => '开启'
        ]
    ];

    /**
     * 数据字段类型 列表
     * @var array
     */
    private static $dataTypeList = [
        [
            'key'   => 'string',
            'value' => 'string',
            'text'  => '文本'
        ],
        [
            'key'   => 'integer',
            'value' => 'integer',
            'text'  => '数字'
        ],
        [
            'key'   => 'image',
            'value' => 'image',
            'text'  => '图片'
        ],
        [
            'key'   => 'url',
            'value' => 'url',
            'text'  => '连接'
        ]
    ];

    /**
     * 最大展示条目
     * @var int
     */
    private static $maxLimit = 999;
    /**
     * 最小展示条目
     * @var int
     */
    private static $minLimit = 1;


    /**
     * 碎片编辑标识
     * @var bool
     */
    private static $BLOCK_EDIT = false;
    /**
     * 检测是否已添加过 添加静态资源
     * @var bool
     */
    private static $isAddJsAsset = false;
    /**
     * 检测是否已添加过 更新静态资源
     * @var bool
     */
    private static $isEditJsAsset = false;


    /**
     * 规则验证
     * @return array
     */
    public function rules()
    {

        $parent = parent::rules();
        // [类型]列表
        $typeList = array_column(self::getTypeList(), 'value');
        // [状态]列表
        $statusList = array_column(self::getStatusList(), 'value');


        return ArrayHelper::merge($parent, [
            ['type', 'in', 'range' => $typeList, 'message' => '类型不合法'],
            ['status', 'in', 'range' => $statusList, 'message' => '状态不合法'],
            ['limit', 'checkLimit']
        ]);
    }

    /**
     * 重写label的 文字
     */
    public function attributeLabels()
    {

        $parent = parent::attributeLabels();
        return array_merge($parent, [
            'id'          => '编号',
            'name'        => '名称',
            'pos'         => '位置',
            'type'        => '类型',
            'limit'       => '碎片展示条数',
            'template'    => 'HTML模板内容',
            'data'        => '数据',
            'data_column' => '数据字段',
            'status'      => '状态',
            'add_time'    => '添加时间',
            'update_time' => '更新时间',
        ]);
    }

    /**
     * 场景
     * @return array
     */
    public function scenarios()
    {

        $scenarios = parent::scenarios();
        return ArrayHelper::merge($scenarios, [
            // 自定义场景 (无用请删除)
            'scUpdate' => [
                'someAttributes'
            ]
        ]);
    }

    /**
     * 检查最大条目数
     * @param null $attributeNames
     * @param bool $clearErrors
     * @return bool
     */
    public function checkLimit($attributeNames = null, $clearErrors = true)
    {
        // 最大数目
        $maxLimit = self::getMaxLimit();
        // 最小条目数 - 和类型有关
        $minLimit = $this->getAttribute('type') == self::getTypeHtml() ?
            0 : self::getMinLimit();
        // 条目数
        $limit = $this->getAttribute('limit');

        // 太大
        if ($limit > $maxLimit) {
            $this->addError('limit', '展示条目不得大于' . $maxLimit);
            return false;
        }

        // 太小
        if ($limit < $minLimit) {
            $this->addError('limit', '展示条目不得小于' . $minLimit);
            return false;
        }

        return true;
    }

    /**
     * 获取分页信息
     * @param integer $page 当前页
     * @param integer $limit 获取几条
     * @param null $field 获取字段
     * @return PageListEntity
     */
    public function getPaginate($page, $limit, $field = null, $opt = [])
    {

        // 当前页面计算
        $page = max($page - 1, 0);

        // 查找的 字段空的 就默认给列表
        if (!$field) $field = '*';

        // 基础 where加载完毕
        $this->getBaseSql()->select($field);

        // 数据的获取 分页等
        $list = $this->getBaseSql()->offset($page * $limit)
            ->limit($limit)
            ->asArray()->all();
        // 格式化数据后的列表
        $list = $this->formatData($list, $opt);

        // 总数
        $total = $this->getBaseSql()->count();

        return PageListEntity::loadModel(compact('list', 'total'));
    }

    /**
     * 格式化列表活详情数据
     * @param array $list 列表
     * @param array $opt 其他设置
     * @return mixed
     */
    public static function formatData($list, $opt = [])
    {

        // 为空直接返回
        if (empty($list)) return $list;
        // 需要返回第一组（可能不是二维数组）
        $needFirst = false;
        if (!is_array(array_values($list)[0])) {
            $needFirst = true;
            $list      = [$list];
        }

        ### 某些参数初始化

        // 格式化数据
        foreach ($list as $k => &$v) {

            // 更新时间
            if (isset($v['add_time'])) {
                $v['add_time_text']   = date('Y-m-d H:i:s', $v['add_time']);
                $v['add_time_text_s'] = date('Y-m-d', $v['add_time']);
            }

            // 更新时间
            if (isset($v['update_time'])) {
                $v['update_time_text']   = date('Y-m-d H:i:s', $v['update_time']);
                $v['update_time_text_s'] = date('Y-m-d', $v['update_time']);
            }

            // 类型 文本
            if (isset($v['type'])) {
                $v['type_text'] = self::getTypeText($v['type']);
            }

            // 状态 文本
            if (isset($v['status'])) {
                $v['status_text'] = self::getStatusText($v['status']);
            }

            if (isset($v['data']) && ConvertService::isJson($v['data'])) {
                $v['data'] = json_decode($v['data'], true);
            }

            if (isset($v['data'])) {
                $v['data'] = FileService::addImgDomain($v['data']);
            }
        }

        reset($list);
        return $needFirst ? current($list) : $list;
    }

    /**
     * 自定义过滤单条[where]
     *  ` 仅限于通过方法[loadWhere]传入的条件, 可以在其中自定义逻辑
     *  ` 保证最终返回值为一维数组[Yii2]通用条件格式[运算符, 字段名, 值]
     * @param $condition array [sql]查询条件, 严格遵循Yii多条件查询:[运算符, 字段名, 值]
     * @return array
     */
    public function whereCustomFilter($condition = [])
    {

        // // 例：字段为编号 —— 操作符使用[IN]
        // if ($field == 'id') {
        //     return ['IN', $condition[1], $value[2]];
        // }

        // 最终返回必须为数组：[运算符, 字段名, 值]，符合Yii2规范
        return $condition;
    }

    /**
     * 添加|保存
     * @param bool $runValidation 是否仅仅运行验证，不运行保存
     * @param string[]|null|string $attributeNames 仅仅保存某几个字段
     * @return bool
     */
    public function save($runValidation = false, $attributeNames = null)
    {

        // 添加的话要赋值一些初始数据
        if (empty($this->id)) {

            // 可以是走[mongoId]
            $this->id = GenerateService::newMongoId();
        }
        ### 单个操作[缓存保存前一些格式化]
        $nowTime = time();
        // 添加时间
        if (empty($this->add_time)) $this->add_time = $nowTime;
        // 更新时间
        $this->update_time = $nowTime;
        $this->data        = FileService::delImgDomain($this->getAttribute('data'));

        ### 批量操作[缓存保存前一些格式化]
        foreach ($this->getAttributes() as $k => $v) {
            // 字段类型为[JSON]类型需要转为数组 - 保存自动转为[JSON]
            if (is_string($v) && ConvertService::isJson($v)) {
                $this->setAttribute($k, json_decode($v, true));
                continue;
            }
        }

        // 检测
        if ($this->hasErrors() || !$this->validate($attributeNames)) {

            // 记录下错误日志
            \Yii::error([
                "`````````````````````````````````````````````````````````",
                "``                      数据库错误                       ``",
                "`` 错误详情: [[碎片]数据库操作模型]验证数据失败             ``",
                "`` 错误信息和参数详情:                                     ``",
                "`````````````````````````````````````````````````````````",
                $this->getAttributes(),
                $this->getErrors()
            ], 'db');
            return false;
        }

        ### 需要 && 执行保存
        if (!$runValidation && !parent::save(false, $attributeNames)) {

            // 记录下错误日志
            \Yii::error([
                "`````````````````````````````````````````````````````````",
                "``                      数据库错误                       ``",
                "`` 错误详情: [[碎片]数据库操作模型]保存数据失败             ``",
                "`` 错误信息和参数详情:                                     ``",
                "`````````````````````````````````````````````````````````",
                $this->getAttributes(),
                $this->getErrors()
            ], 'db');
            return false;
        }

        return true;
    }

    /**
     * [静态方法]批量快速更新某些字段
     * @param $condition
     * @param array $fieldVal
     * @return bool
     */
    public static function updateField($condition, $fieldVal = [])
    {

        // 验证字段
        $model = self::loadModel();
        $model->load($fieldVal, '');
        if (!$model->validate(array_keys($fieldVal))) {
            $error                          = UtilsService::getModelError($model->getErrors());
            self::$error_[$error['column']] = [$error['msg']];
            return false;
        }
        // 重新取值规则化后的特定值列表
        $fieldVal = $model->getAttributes(array_keys($fieldVal));

        $db = \Yii::$app->db->createCommand();

        try {

            $db->update(self::tableName(), $fieldVal, $condition)->execute();

            // 否则成功
            return true;
        } catch (\Exception $error) {

            // 记录下错误日志
            \Yii::error([
                "`````````````````````````````````````````````````````````",
                "``                      数据库错误                       ``",
                "`` 错误详情: [[碎片]数据库操作模型]批量修改[指定字段]失败，   ``",
                "``         {$error->getMessage()}                       ``",
                "`` SQL语句: {$db->getRawSql()}                         ``",
                "`` 错误信息和参数详情:                                     ``",
                "`````````````````````````````````````````````````````````",
                $error->getTraceAsString()
            ], 'db');

            // 静态错误
            self::$error_['db_error'] = empty(self::$error_['db_error']) ?
                [$error->getMessage()] : array_merge(self::$error_['db_error'], [$error->getMessage()]);

            return false;
        }
    }

    /**
     * 批量添加数据
     * @param array $createData
     * @return bool
     */
    public static function createData($createData = [])
    {

        $db = \Yii::$app->db->createCommand();
        try {

            ### 先数据格式化
            // 如果是一维数组则二维数组化
            if (ArrayHelper::isAssociative($createData)) {
                $createData = [$createData];
            }
            $values = [];
            foreach ($createData as $k => $v) {

                $model = self::loadModel();
                $model->load($createData[$k], '');
                if (!$model->save(true)) {
                    // 取出错误信息
                    $error = UtilsService::getModelError($model->errors);
                    // 添加到静态方法上
                    self::$error_[$error['column']] = [$error['msg']];
                    return false;
                }

                $createData[$k] = $model->getAttributes($model::getTableSchema()->getColumnNames());

                // 循环一些数据
                foreach ($createData[$k] as $kc => $vc) {
                    // 字段类型为[JSON]类型需要转为[JSON]
                    if (is_array($vc)) {
                        $createData[$k][$kc] = json_encode($vc, JSON_UNESCAPED_UNICODE);
                        continue;
                    }
                }

                // 值赋值
                $values[] = array_values($createData[$k]);;
            }

            ### 取出此次操作的字段列表
            $columns = !current($createData) ? [] : array_keys(current($createData));

            // 执行
            $addResult = $db->batchInsert(self::tableName(), $columns, $values)->execute();

            return $addResult;
        } catch (\Exception $error) {

            // 记录下错误日志
            \Yii::error([

                "`````````````````````````````````````````````````````````",
                "``                      数据库错误                       ``",
                "`` 错误详情: [[碎片]数据库操作模型]批量添加[数据]失败，      ``",
                "``         {$error->getMessage()}                       ``",
                "`` SQL语句: {$db->getRawSql()}                         ``",
                "`` 错误信息和参数详情:                                     ``",
                "`````````````````````````````````````````````````````````",
                $error->getTraceAsString()
            ], 'db');

            // 静态错误
            self::$error_['db_error'] = empty(self::$error_['db_error']) ?
                [$error->getMessage()] : array_merge(self::$error_['db_error'], [$error->getMessage()]);

            return false;
        }
    }

    /**
     * 更新某些字段自增|自减
     * @param $condition
     * @param array $fieldVal 增/减加的字段
     * @return bool
     */
    public static function updateCounter($condition, $fieldVal = [])
    {

        $model = new self();
        foreach ($fieldVal as $k => $v) {

            if (!$model->hasAttribute($k)) {

                unset($fieldVal[$k]);
                continue;
            }
        }

        try {

            $model->updateAllCounters($fieldVal, $condition);

            // 否则成功
            return true;
        } catch (\Exception $error) {

            // 记录下错误日志
            \Yii::error([
                "`````````````````````````````````````````````````````````",
                "``                      数据库错误                       ``",
                "`` 错误详情: [[碎片]数据库操作模型]批量增/减[指定字段]失败   ``",
                "``         {$error->getMessage()}                       ``",
                "`` 错误信息和参数详情:                                     ``",
                "`````````````````````````````````````````````````````````",
                $error->getTraceAsString()
            ], 'db');

            // 静态错误
            self::$error_['db_error'] = empty(self::$error_['db_error']) ?
                [$error->getMessage()] : array_merge(self::$error_['db_error'], [$error->getMessage()]);

            return false;
        }
    }


    /**
     * 获取[类型][自定义静态文本]文本
     * @return mixed|string
     */
    public static function getTypeHtml()
    {

        $list = array_column(self::$typeList, null, 'key');
        return $list['html']['value'];
    }

    /**
     * 获取[类型]文本
     * @param $value
     * @return mixed|string
     */
    public static function getTypeText($value)
    {

        // 列表
        $list = array_column(self::$typeList, null, 'value');
        // 不合法 - 不存在
        if (empty($list[$value]['text']))

            return '--';

        // 最终正常返回
        return $list[$value]['text'];
    }

    /**
     * 获取[类型]列表 值
     * @return mixed|string
     */
    public static function getTypeList()
    {

        // 最终正常返回
        return self::$typeList;
    }


    /**
     * 获取[数据字段类型]图片 值
     * @param $value
     * @return mixed|string
     */
    public static function getDataTypeImage()
    {

        $list = array_column(self::$dataTypeList, null, 'key');
        return $list['image']['value'];
    }

    /**
     * 获取[数据字段类型]文本
     * @param $value
     * @return mixed|string
     */
    public static function getDataTypeText($value)
    {

        // 列表
        $list = array_column(self::$dataTypeList, null, 'value');
        // 不合法 - 不存在
        if (empty($list[$value]['text']))

            return '--';

        // 最终正常返回
        return $list[$value]['text'];
    }

    /**
     * 获取[数据字段类型]列表 值
     * @return mixed|string
     */
    public static function getDataTypeList()
    {

        // 最终正常返回
        return self::$dataTypeList;
    }


    /**
     * 获取[状态][默认]值
     * @return mixed
     */
    public static function getStatusDefault()
    {

        $list = array_column(self::$statusList, null, 'key');
        return $list['default']['value'];
    }

    /**
     * 获取[状态][关闭]值
     * @return mixed
     */
    public static function getStatusDisabled()
    {

        $list = array_column(self::$statusList, null, 'key');
        return $list['disabled']['value'];
    }

    /**
     * 获取[状态][开启]值
     * @return mixed
     */
    public static function getStatusOpen()
    {

        $list = array_column(self::$statusList, null, 'key');
        return $list['open']['value'];
    }

    /**
     * 获取[状态]文本
     * @param $value
     * @return mixed|string
     */
    public static function getStatusText($value)
    {

        // 列表
        $list = array_column(self::$statusList, null, 'value');
        // 不合法 - 不存在
        if (empty($list[$value]['text']))

            return '--';

        // 最终正常返回
        return $list[$value]['text'];
    }

    /**
     * 获取[状态]列表 值
     * @return mixed|string
     */
    public static function getStatusList()
    {

        // 最终正常返回
        return self::$statusList;
    }


    /**
     * 获取展示最大条目数
     * @return int
     */
    public static function getMaxLimit()
    {
        return self::$maxLimit;
    }

    /**
     * 获取展示最小条目数
     * @return int
     */
    public static function getMinLimit()
    {
        return self::$minLimit;
    }


    /**
     * 赋值状态 碎片编辑中...
     */
    public static function blockIsEditing()
    {
        self::$BLOCK_EDIT = true;
    }

    /**
     * 获取静态错误
     * @return mixed
     */
    public static function getStaticErrors()
    {
        return self::$error_;
    }


    /**
     * 获取碎片|碎片内容 - 放在[HTML]中
     * @param string $posName
     * @return false|string|string[]|null
     */
    public static function block($posName = '')
    {

        ### 现根据碎片位置 查询缓存
        $html = self::getBlockHtmlCache($posName);
        if (!self::$BLOCK_EDIT && !empty($html)) {
            return strstr($html, 'data-has-error') ? '' : $html;
        }

        // 在查询下碎片 信息
        $model = self::loadModel(['pos' => $posName]);

        ### 碎片未找到
        // 数据不存在 - 编辑者返回添加数据
        if (empty($model) && self::$BLOCK_EDIT) return self::getAddBlockHtml($posName);
        // 数据不存在 - 普通用户返回空内容
        if (empty($model) && !self::$BLOCK_EDIT) return '';

        ### 碎片存在
        // 渲染
        $html = self::renderBlockHtml($model->getAttributes());
        // 存储下缓存
        self::setBlockHtmlCache($posName, $html);

        // 第一次加载、非编辑人员 - 直接返回内容
        if (!self::$BLOCK_EDIT && !empty($html)) {
            return strstr($html, 'data-has-error') ? '' : $html;
        }

        // 将其中的[href]清空
        $html = preg_replace('/href=\s*[\'|\"](.*?)[\'|\"]/', '', $html);

        ### 如果是管理者返回编辑碎片[HTML]
        if (self::$BLOCK_EDIT) {
            $html = self::getEditBlockHtml($model->getAttribute('id'), $html);
        }

        return $html;
    }

    /**
     * 获取碎片的内容
     * @param $posName
     * @return string
     */
    public static function getBlockHtmlCache($posName)
    {

        $cache = CacheService::loadModel([
            'type'     => CacheService::getTypeMemcache(),  // 类型
            'cacheKey' => CacheService::getCacheKey('block') // 键
        ]);

        // 值
        $value = $cache->get();

        // [HTML]数据是否为空，空的返回
        if (empty($value[$posName]['html'])) return '';

        // 返回[HTML]
        return $value[$posName]['html'];
    }

    /**
     * 写入模板内容的缓存
     * @param string $posName 位置名称
     * @param string $html 缓存[HTML]
     * @return bool
     */
    public static function setBlockHtmlCache($posName = '', $html = '')
    {

        $cache = CacheService::loadModel([
            'type'     => CacheService::getTypeMemcache(),  // 类型
            'cacheKey' => CacheService::getCacheKey('block') // 键
        ]);

        // 值
        $value = $cache->get();
        // 默认赋值
        if (empty($cache)) $cache = [];

        // 缓存时间
        $times = time();

        $value[$posName] = [
            'html'            => $html,
            'cache_time'      => $times,
            'cache_time_text' => date('Y-m-d H:i:s', $times)
        ];

        // 返回结果
        if (!$cache->set(false, $value)) {

            $error = UtilsService::getModelError($cache->errors);
            // 记录下错误日志
            \Yii::error([

                "`````````````````````````````````````````````````````````",
                "``                      缓存错误                         ``",
                "`` 错误详情：[碎片] 缓存失败, 请尽快排查原因                  ``",
                "`` 错误信息和参数详情:                                     ``",
                "`````````````````````````````````````````````````````````",
                $error['msg']
            ], 'db');

            return false;
        }

        return true;
    }

    /**
     * 获取添加碎片[HTML]
     * @param string $posName 推荐位名称
     * @return string
     */
    public static function getAddBlockHtml($posName = '')
    {

        $html = <<<EOT
            
<div class="">
    
EOT;

        // 未添加JS方法 添加即可
        if (!self::$isAddJsAsset) {
            $html .= <<<EOT
        
    <style>
        .ADD_BLOCK_WRAPPER {
            position:relative;
            width: 100%;
            min-width: 100px;
            height: 100%;
            min-height: 35px;
            background-color: rgba(63, 158, 255, 0.3);
            box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
            cursor: pointer;
        }
        .ADD_BLOCK_TEXT {
            width: 70px;
            height: 35px;
            line-height: 35px;
            text-align: center;
            position:absolute;
            top: 0;
            right: 0;
            z-index: 10;
            background-color: #67c23a;
            white-space: nowrap;
            color: white;
            box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
            font-weight:normal;
            font-size: 12px;
        }
    </style>

EOT;
        }

        $html .= <<<EOT
    <div class="ADD_BLOCK_WRAPPER" onclick="parentClickAdd('{$posName}')">
        <div class="ADD_BLOCK_TEXT">
            <span>添加碎片</span>
        </div>
    </div>

EOT;
        // 未添加JS方法 添加即可
        if (!self::$isAddJsAsset) {

            $html .= <<<EOT
            
    <script type="text/javascript">
        function parentClickAdd(posName) {
            if (!parent.window.instance) return console.error('父级信息实例未找到');
            parent.window.instance.addBlock(posName);
        }
    </script>

EOT;
        }
        $html .= <<<EOT
        
</div>

EOT;
        // 已经添加过[JS]方法
        self::$isAddJsAsset = true;
        return $html;
    }

    /**
     * 获取编辑碎片[HTML]
     * @param string $posId
     * @param string $renderHtml
     * @return string
     */
    public static function getEditBlockHtml($posId = '', $renderHtml = '')
    {


        $html = <<<EOT
        
<div>
EOT;
        if (!self::$isEditJsAsset) {
            $html .= <<<EOT
            
    <style>
        .EDIT_BLOCK_WRAPPER {
            position:relative;
            width: 100%;
            min-width: 100px;
            /*height: 100%;*/
            min-height: 35px;
            background-color: rgba(63, 158, 255, 0.3);
            box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
            cursor: pointer;
        }
        .EDIT_BLOCK_TEXT {
            width: 70px;
            height: 35px;
            line-height: 35px;
            text-align: center;
            position:absolute;
            top: 0;
            right: 0;
            z-index: 10;
            background-color: #409EFF;
            white-space: nowrap;
            color: white;
            box-shadow: 0 2px 4px rgba(0, 0, 0, .12), 0 0 6px rgba(0, 0, 0, .04);
            font-weight:normal;
            font-size: 12px;
        }
    </style>
EOT;
        }
        $html .= <<<EOT

    <div class="EDIT_BLOCK_WRAPPER pointer"
        onclick="parentClickEdit('{$posId}')">
        <div class="EDIT_BLOCK_TEXT">
            <span>编辑碎片</span>
        </div>
        $renderHtml
    </div>
EOT;
        if (!self::$isEditJsAsset) {
            $html .= <<<EOT

    <script type="text/javascript">
        function parentClickEdit(posId) {
            if (!parent.window.instance) return console.error('父级信息实例未找到');
            parent.window.instance.editBlock(posId);
        }
    </script>
EOT;
        }
        $html .= <<<EOT

</div>

EOT;
        // 已经添加过[JS]方法
        self::$isEditJsAsset = true;
        return $html;
    }

    /**
     * 渲染碎片[HTML]
     * @param array $data 数据详情
     * @param boolean $isReload 是否刷新
     * @return false|string
     */
    public static function renderBlockHtml($data = [])
    {

        ### 首先类型为
        // 碎片编号
        $blockId = empty($data['id']) ? '' : $data['id'];

        try {

            // 模板解密下
            $template = '';
            if (!empty($data['template'])) {
                $template = htmlspecialchars_decode($data['template']);
            }

            // 左侧定界符
            $lefDeli = empty(
            \Yii::$app->components['view']['renderers']['tpl']['options']['left_delimiter']
            ) ? null : \Yii::$app->components['view']['renderers']['tpl']['options']['left_delimiter'];
            // 右侧定界符
            $rightDeli = empty(
            \Yii::$app->components['view']['renderers']['tpl']['options']['right_delimiter']
            ) ? null : \Yii::$app->components['view']['renderers']['tpl']['options']['right_delimiter'];
            // smarty 进行渲染
            $smarty = new \Smarty();
            // 赋值 合成文件夹
            $smarty->setCompileDir(\Yii::getAlias('@runtime/smarty/block/compile'));
            // 赋值合成文件名称 ID
            $smarty->setCompileId($data['pos']);
            // 赋值 缓存文件夹
            $smarty->setCacheDir(\Yii::getAlias('@runtime/page-cache/cache'));
            // 赋值 左侧定界符
            $smarty->setLeftDelimiter($lefDeli);
            // 赋值 右侧侧定界符
            $smarty->setRightDelimiter($rightDeli);
            // 赋追是否开启 缓存(缓存走 memcache。。。已经实现 在修改碎片的时候更新缓存)
            $smarty->caching = false;

            ob_start(); // 打开缓冲区

            // 赋值 数据
            $smarty->assign([
                'data' => empty($data['data']) ? [] : $data['data']
            ]);

            // 渲染 HTML
            $smarty->display('string:' . $template);

            $template = ob_get_contents();

            // 关闭缓冲
            ob_end_clean();

            return $template;

        } catch (\Exception $err) {

            // 记录下错误日志
            \Yii::error([
                "************************* 数据库错误 *************************",
                "错误：[渲染碎片] - [编号：{$blockId}] 出错",
                "\n\n*** 数据列表: *** \n",
                $data,
                "\n\n*** 错误详情: *** \n",
                "\n{$err->getMessage()}\n",
                $err->getTraceAsString()
            ], 'db');

            $errorHtml =
                '<span class="text-danger" data-has-error="true">' .
                '出错：' . $err->getMessage() .
                '</span>';
            // 替换为空
            return $errorHtml;
        }

    }
}
